<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>新能源汽车舆情分析系统</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/wordcloud2.js/1.2.2/wordcloud2.min.js"></script>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: 'Microsoft YaHei', Arial, sans-serif; background-color: #f5f5f5; color: #333; }
        .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px 0; text-align: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        .header h1 { font-size: 2.2em; margin-bottom: 6px; }
        .header p { opacity: 0.9; }
        .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
        .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; margin-bottom: 24px; }
        .stat-card { background: #fff; padding: 20px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.06); text-align: center; }
        .stat-card h3 { color: #667eea; font-size: 1.8em; margin-bottom: 6px; }
        .chart-container { background: #fff; padding: 20px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.06); margin-bottom: 24px; }
        .chart-title { font-size: 1.3em; margin-bottom: 12px; color: #333; text-align: center; }
        #sentimentChart{ max-height: 220px; }
        .controls { background: #fff; padding: 16px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.06); margin-bottom: 24px; }
        .btn { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; border: none; padding: 10px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; margin-right: 10px; }
        .btn:hover { opacity: .92; }
        .btn.secondary { background: #6c757d; }
        .tabs { display: flex; gap: 8px; margin: 10px 0 16px; }
        .tab-btn { padding: 8px 14px; border-radius: 16px; border: 1px solid #ddd; background: #fff; cursor: pointer; font-size: 14px; }
        .tab-btn.active { background: #667eea; color: #fff; border-color: #667eea; }
        .articles-list { background: #fff; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.06); overflow: hidden; }
        .articles-header { background: #f8f9fa; padding: 16px; border-bottom: 1px solid #eee; }
        .article-item { padding: 16px; border-bottom: 1px solid #eee; }
        .article-title { font-weight: bold; margin-bottom: 8px; }
        .article-meta { color: #666; font-size: 12px; margin-bottom: 8px; }
        .article-content { color: #555; line-height: 1.6; }
        .loading { text-align: center; padding: 30px; color: #666; }
        .error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 6px; margin: 16px 0; }
    </style>
</head>
<body>
    <div class="header">
        <h1>🚗 新能源汽车舆情分析系统</h1>
        <p>基于大模型的智能舆情监测与分析平台</p>
    </div>

    <div class="container">
        <div class="controls">
            <h3 style="margin-bottom: 10px;">系统控制</h3>
            <button class="btn" onclick="startCollection()">开始数据采集</button>
            <button class="btn" onclick="startAnalysis()">开始舆情分析</button>
            <button class="btn" onclick="generateReport()">生成分析报告</button>
            <button class="btn secondary" onclick="refreshData()">刷新数据</button>
        </div>

        <!-- 顶部标签导航 -->
        <div class="tabs">
            <button class="tab-btn active" data-tab="visual" onclick="switchTab('visual')">可视化</button>
            <button class="tab-btn" data-tab="articles" onclick="switchTab('articles')">最近文章</button>
        </div>

        <!-- 可视化 Tab -->
        <div id="tab-visual">
        <div class="stats-grid">
            <div class="stat-card">
                <h3 id="totalArticles">-</h3>
                <div>总文章数</div>
            </div>
            <div class="stat-card">
                <h3 id="positiveCount">-</h3>
                <div>正面情感</div>
            </div>
            <div class="stat-card">
                <h3 id="negativeCount">-</h3>
                <div>负面情感</div>
            </div>
            <div class="stat-card">
                <h3 id="neutralCount">-</h3>
                <div>中性情感</div>
            </div>
        </div>

        <div class="chart-container">
            <h3 class="chart-title">情感分布</h3>
            <canvas id="sentimentChart"></canvas>
        </div>

        <div class="chart-container">
            <h3 class="chart-title">情感趋势</h3>
            <canvas id="trendChart" width="400" height="200"></canvas>
        </div>

        <div class="chart-container">
            <h3 class="chart-title">每日正/负/中分布</h3>
            <canvas id="dailyBreakdownChart"></canvas>
        </div>

        <div class="chart-container">
            <h3 class="chart-title">关键词词云</h3>
            <div id="wordCloud" style="height:320px;"></div>
        </div>
        </div>

        <!-- 最近文章 Tab -->
        <div id="tab-articles" style="display:none;">
        <div class="articles-list">
            <div class="articles-header">
                <h3>最近文章</h3>
            </div>
            <div id="articlesList"><div class="loading">加载中...</div></div>
            <div id="articlesPager" style="display:flex; align-items:center; justify-content:center; gap:10px; padding:12px;">
                <button class="btn secondary" onclick="prevPage()">上一页</button>
                <span id="pageInfo">-/-</span>
                <button class="btn secondary" onclick="nextPage()">下一页</button>
            </div>
        </div>
        </div>
    </div>

    <!-- 报告弹窗 -->
    <div id="reportModal" style="display:none; position:fixed; inset:0; background: rgba(0,0,0,0.4); z-index: 1000;">
        <div style="background:#fff; width: 80%; max-width: 900px; margin: 60px auto; border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); overflow:hidden;">
            <div style="padding:14px 18px; border-bottom:1px solid #eee; display:flex; align-items:center; justify-content:space-between;">
                <h3 style="margin:0; font-size:18px;">舆情分析报告</h3>
                <div>
                    <button id="downloadReportBtn" class="btn" style="padding:6px 12px; font-size:0.9em;">下载</button>
                    <button onclick="hideReportModal()" class="btn secondary" style="padding:6px 12px; font-size:0.9em;">关闭</button>
                </div>
            </div>
            <div id="reportBody" style="padding:18px; max-height: 70vh; overflow:auto;">
                <div id="reportLoading" class="loading">报告生成中，请稍等...</div>
                <pre id="reportContent" style="display:none; white-space: pre-wrap; font-family: Menlo, Consolas, monospace; line-height:1.5;"></pre>
            </div>
        </div>
    </div>

    <script>
        let sentimentChart, trendChart, dailyChart, cachedArticles = [];
        let currentPage = 1, pageSize = 10, totalArticles = 0;

        document.addEventListener('DOMContentLoaded', function () {
            initCharts();
            loadDashboardData();
        });

        async function loadDashboardData() {
            try {
                const statsResponse = await axios.get('/api/sentiment/stats?days=7');
                updateStatsCards(statsResponse.data);
                updateSentimentChart(statsResponse.data);

                const trendsResponse = await axios.get('/api/sentiment/trends?days=30');
                updateTrendChart(trendsResponse.data.trends);

                await loadArticles(1);
                await loadKeywords();
                // 每日分布
                const dailyResp = await axios.get('/api/sentiment/daily_breakdown?days=30');
                updateDailyChart(dailyResp.data.daily || []);
            } catch (error) {
                console.error('加载数据失败:', error);
                showError('加载数据失败: ' + error.message);
            }
        }

        function updateStatsCards(stats) {
            document.getElementById('totalArticles').textContent = stats.total_articles || 0;
            const distribution = stats.sentiment_distribution || {};
            document.getElementById('positiveCount').textContent = distribution.正面?.count || 0;
            document.getElementById('negativeCount').textContent = distribution.负面?.count || 0;
            document.getElementById('neutralCount').textContent = distribution.中性?.count || 0;
        }

        function initCharts() {
            const sentimentCtx = document.getElementById('sentimentChart').getContext('2d');
            sentimentChart = new Chart(sentimentCtx, {
                type: 'doughnut',
                data: { labels: ['正面', '负面', '中性'], datasets: [{ data: [0, 0, 0], backgroundColor: ['#28a745', '#dc3545', '#6c757d'] }] },
                options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } }
            });

            const trendCtx = document.getElementById('trendChart').getContext('2d');
            trendChart = new Chart(trendCtx, {
                type: 'line',
                data: {
                    labels: [],
                    datasets: [
                        { label: '文章数量', yAxisID: 'y', data: [], borderColor: '#667eea', backgroundColor: 'rgba(102, 126, 234, 0.1)', tension: 0.4 },
                        { label: '平均情感(0-1)', yAxisID: 'y1', data: [], borderColor: '#28a745', backgroundColor: 'rgba(40, 167, 69, 0.1)', tension: 0.4 }
                    ]
                },
                options: {
                    responsive: true,
                    interaction: { mode: 'index', intersect: false },
                    stacked: false,
                    plugins: { legend: { position: 'bottom' } },
                    scales: {
                        y: { type: 'linear', position: 'left', beginAtZero: true, title: { display: true, text: '文章数量' } },
                        y1: { type: 'linear', position: 'right', beginAtZero: true, min: 0, max: 1, grid: { drawOnChartArea: false }, title: { display: true, text: '平均情感' } }
                    }
                }
            });

            // 每日分布堆叠柱状图
            const dailyCtx = document.getElementById('dailyBreakdownChart').getContext('2d');
            dailyChart = new Chart(dailyCtx, {
                type: 'bar',
                data: { labels: [], datasets: [
                    { label: '正面', data: [], backgroundColor: '#28a745', stack: 'daily' },
                    { label: '负面', data: [], backgroundColor: '#dc3545', stack: 'daily' },
                    { label: '中性', data: [], backgroundColor: '#6c757d', stack: 'daily' }
                ]},
                options: { responsive: true, plugins: { legend: { position: 'bottom' } }, scales: { x: { stacked: true }, y: { stacked: true, beginAtZero: true } } }
            });
        }

        function updateSentimentChart(stats) {
            const distribution = stats.sentiment_distribution || {};
            const data = [distribution.正面?.count || 0, distribution.负面?.count || 0, distribution.中性?.count || 0];
            sentimentChart.data.datasets[0].data = data;
            sentimentChart.update();
        }

        function updateTrendChart(trends) {
            const labels = (trends || []).map(t => t.date);
            const counts = (trends || []).map(t => t.article_count ?? 0);
            const avg = (trends || []).map(t => (typeof t.avg_sentiment === 'number' ? t.avg_sentiment : null));
            trendChart.data.labels = labels;
            trendChart.data.datasets[0].data = counts;
            trendChart.data.datasets[1].data = avg;
            trendChart.update();
        }

        function updateArticlesList(articles) {
            const container = document.getElementById('articlesList');
            if (!articles || articles.length === 0) {
                container.innerHTML = '<div class="loading">暂无文章数据</div>';
                return;
            }
            const html = articles.map(article => `
                <div class="article-item">
                    <div class="article-title">
                        ${article.url ? `<a href="${article.url}" target="_blank" rel="noopener noreferrer">${article.title || '无标题'}</a>` : (article.title || '无标题')}
                    </div>
                    <div class="article-meta">来源: ${article.source || '未知'} | 时间: ${article.publish_time || '未知'} | 字数: ${article.word_count || 0}</div>
                    <div class="article-content">${(article.content || '').substring(0, 200)}...</div>
                </div>
            `).join('');
            container.innerHTML = html;
            const totalPages = Math.max(1, Math.ceil(totalArticles / pageSize));
            document.getElementById('pageInfo').textContent = `${currentPage}/${totalPages}`;
        }

        async function loadArticles(page) {
            currentPage = page;
            const skip = (currentPage - 1) * pageSize;
            const resp = await axios.get(`/api/articles?limit=${pageSize}&skip=${skip}`);
            cachedArticles = resp.data.articles || [];
            totalArticles = resp.data.total || cachedArticles.length;
            updateArticlesList(cachedArticles);
        }
        async function nextPage() {
            const totalPages = Math.max(1, Math.ceil(totalArticles / pageSize));
            if (currentPage < totalPages) {
                await loadArticles(currentPage + 1);
            }
        }
        async function prevPage() {
            if (currentPage > 1) {
                await loadArticles(currentPage - 1);
            }
        }

        // 更新关键词词云
        async function loadKeywords() {
            const wc = document.getElementById('wordCloud');
            if (!wc) return;
            try {
                const resp = await axios.get('/api/keywords?days=30&limit=50');
                const data = resp.data.keywords || [];
                if (!data.length) { wc.innerHTML = '<div class="loading">暂无关键词</div>'; return; }
                const max = data[0]?.count || 1;
                const list = data.map(k => [k.keyword, k.count]);
                WordCloud(wc, {
                    list,
                    gridSize: 8,
                    weightFactor: (size) => 10 + (size / max) * 24,
                    color: () => `hsl(${Math.floor(Math.random()*360)},70%,45%)`,
                    rotateRatio: 0,
                    backgroundColor: '#ffffff'
                });
            } catch (e) {
                wc.innerHTML = '<div class="loading">加载关键词失败</div>';
            }
        }

        function updateDailyChart(daily) {
            const labels = daily.map(d => d.date);
            const pos = daily.map(d => d['正面'] || 0);
            const neg = daily.map(d => d['负面'] || 0);
            const neu = daily.map(d => d['中性'] || 0);
            dailyChart.data.labels = labels;
            dailyChart.data.datasets[0].data = pos;
            dailyChart.data.datasets[1].data = neg;
            dailyChart.data.datasets[2].data = neu;
            dailyChart.update();
        }

        // 标签切换
        function switchTab(tab) {
            document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
            const btn = document.querySelector(`.tab-btn[data-tab="${tab}"]`);
            if (btn) btn.classList.add('active');
            document.getElementById('tab-visual').style.display = tab === 'visual' ? 'block' : 'none';
            document.getElementById('tab-articles').style.display = tab === 'articles' ? 'block' : 'none';
        }

        async function startCollection() {
            try { const r = await axios.post('/api/collect'); alert('数据采集已启动: ' + r.data.message); }
            catch (e) { alert('启动数据采集失败: ' + e.message); }
        }

        async function startAnalysis() {
            try { const r = await axios.post('/api/analyze'); alert('舆情分析已启动: ' + r.data.message); }
            catch (e) { alert('启动舆情分析失败: ' + e.message); }
        }

        async function generateReport() {
            showReportModal();
            try {
                const response = await axios.get('/api/report?days=7');
                const report = response.data.report || '';
                setReportContent(report);
            } catch (error) {
                setReportContent('生成报告失败: ' + (error?.message || '未知错误'));
            }
        }

        function showReportModal() {
            const modal = document.getElementById('reportModal');
            const loading = document.getElementById('reportLoading');
            const content = document.getElementById('reportContent');
            modal.style.display = 'block';
            loading.style.display = 'block';
            content.style.display = 'none';
            content.textContent = '';
            const btn = document.getElementById('downloadReportBtn');
            if (btn) btn.onclick = downloadCurrentReport;
        }

        function hideReportModal() { document.getElementById('reportModal').style.display = 'none'; }

        function setReportContent(text) {
            const loading = document.getElementById('reportLoading');
            const content = document.getElementById('reportContent');
            loading.style.display = 'none';
            content.style.display = 'block';
            content.textContent = text;
            content.setAttribute('data-report', text);
        }

        function downloadCurrentReport() {
            const content = document.getElementById('reportContent').getAttribute('data-report') || '';
            const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a'); a.href = url;
            const ts = new Date(); const pad = n => (n < 10 ? '0' + n : '' + n);
            const name = `sentiment_report_${ts.getFullYear()}${pad(ts.getMonth()+1)}${pad(ts.getDate())}_${pad(ts.getHours())}${pad(ts.getMinutes())}.txt`;
            a.download = name; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
        }

        function refreshData() { loadDashboardData(); }

        function showError(message) {
            const container = document.querySelector('.container');
            const errorDiv = document.createElement('div');
            errorDiv.className = 'error';
            errorDiv.textContent = message;
            container.insertBefore(errorDiv, container.firstChild);
            setTimeout(() => { errorDiv.remove(); }, 5000);
        }
    </script>
</body>
</html>
